﻿//#define _CRT_SECURE_NO_WARNINGS
//#include <stdio.h>
//#include <string.h>

#include "common.h"

void test_work_two()
{
	//指针、数组的基础复习题
	{
		print_start_line("指针、数组的基础复习题部分开始");
		int a[] = { 1,2,3,4 };
		printf("int a[] = { 1,2,3,4 };\n");
		printf("sizeof(a) = %zd\n", sizeof(a));// 16
		//sizeof(数组名),数组名表示整个数组,计算的是整个数组的大小,单位是字节
		printf("sizeof(a + 0) = %zd\n", sizeof(a + 0));// 4 or 8
		//a不是单独放在 sizeof 内部,也没有取地址,所以a就是首元素的地址,
		// a+0 还是首元素的地址，是地址,大小就是 4 或 8 个字节
		printf("sizeof(*a) = %zd\n", sizeof(*a));// 4
		//*a 中的 a 是数组首元素的地址,*a 就是对首元素的地址解引用,找到的就是首元素
		//首元素的大小就是 4 个字节
		printf("sizeof(a + 1) = %zd\n", sizeof(a + 1));
		//这里的 a 是数组首元素的地址
		//a+1 是第二个元素的地址
		//sizeof(a+1) 就是地址的大小
		printf("sizeof(a[1]) = %zd\n", sizeof(a[1]));// 4
		//计算的是第二个元素的大小
		printf("sizeof(&a) = %zd\n", sizeof(&a));// 4 or 8
		//&a 取出的是数组的地址，数组的地址也就是个地址，大小就是 16 个字节
		printf("sizeof(*&a) = %zd\n", sizeof(*&a));// 16
		//a 的类型: int (*)[4]，数组指针解引用找到的是数组
		printf("sizeof(&a + 1) = %zd\n", sizeof(&a + 1));// 4 or 8
		//&a+1 指向的是跳过整个数组之后的地址，但还是指针，大小是 4 或 8 个字节
		printf("sizeof(&a[0]) = %zd\n", sizeof(&a[0]));// 4 or 8
		//&a[0] 指向的是数组的首元素的地址，大小是 4 或 8 个字节
		printf("sizeof(&a[0] + 1) = %zd\n", sizeof(&a[0] + 1)); // 4 or 8
		//&a[0]+1 指向的是第二个元素的地址，大小是 4 或 8 个字节
		printf("sizeof(a[0] + 1) = %zd\n", sizeof(a[0] + 1));
		// 4，int 类型，大小是 4 个字节
	//printf("%d\n", strlen('a')); // 'a' --> 97，这里是野指针
		printf("-------------------------------------\n");

		//字符串的情况
		//char arr[] = { 'a', 'b', 'c', 'd', 'e', 'f' };
		char arr[] = "abcdef";
		printf("char arr[] = \"abcdef\";\n");
		//[a b c d e f \0]
		printf("sizeof(arr) = %zd\n", sizeof(arr));//7
		printf("sizeof(arr + 0) = %zd\n", sizeof(arr + 0));//4 or 8
		printf("sizeof(*arr) = %zd\n", sizeof(*arr));//1
		printf("sizeof(arr[1]) = %zd\n", sizeof(arr[1]));//1
		printf("sizeof(&arr) = %zd\n", sizeof(&arr));//4 or 8
		printf("sizeof(&arr + 1) = %zd\n", sizeof(&arr + 1));//4 or 8
		printf("sizeof(&arr[0] + 1) = %zd\n", sizeof(&arr[0] + 1));//4 or 8
		//printf("%d\n", strlen(*arr + 1));//野指针
		printf("-------------------------------------\n");

		char* p = "abcdef";
		printf("char* p = \"abcdef\";\n");
		printf("sizeof(p) = %zd\n", sizeof(p)); // 4 or 8，指向 a
		printf("sizeof(p + 1) = %zd\n", sizeof(p + 1)); // 4 or 8，指向 b
		printf("sizeof(*p) = %zd\n", sizeof(*p)); // 1
		printf("sizeof(p[0]) = %zd\n", sizeof(p[0])); // 1
		printf("sizeof(&p) = %zd\n", sizeof(&p));
		// 4 or 8，二级指针，指向 p 但不是 p 指向的地址
		printf("sizeof(&p + 1) = %zd\n", sizeof(&p + 1)); // 4 or 8，二级指针，
		printf("sizeof(&p[0] + 1) = %zd\n", sizeof(&p[0] + 1));
		// 4 or 8，一级指针，指向 b
		printf("-------------------------------------\n");

		printf("strlen(p) = %lld\n", strlen(p)); // 6
		printf("strlen(p + 1) = %lld\n", strlen(p + 1)); // 5
		//printf("%lld\n", strlen(*p)); // 野指针
		//printf("%lld\n", strlen(p[0])); // 野指针
		printf("strlen(&p) = %lld\n", strlen(&p));
		// 随机值，因为这里二级指针不知道指向的地址
		printf("strlen(&p + 1) = %lld\n", strlen(&p + 1));
		// 随机值，值可能与上一个随机值有关
		printf("strlen(&p[0] + 1) = %lld\n", strlen(&p[0] + 1)); // 5

		print_end_line("指针、数组的基础复习题部分结束");
	}

	// 二维数组的情况
	{
		print_start_line("二维数组的情况复习题部分开始");
		int a[3][4] = { {1,2,3,4},{5,6,7,8},{9,10,11,12} };
		printf("int a[3][4] = { {1,2,3,4},{5,6,7,8},{9,10,11,12} };\n");
		printf("sizeof(a) = %zd\n", sizeof(a)); // 48，整个数组的大小
		printf("sizeof(a[0][0]) = %zd\n", sizeof(a[0][0]));
		// 4 对应 int 1 大小是 4 个字节 int 类型，
		printf("sizeof(a[0]) = %zd\n", sizeof(a[0]));
		// 16，a[0] 是整个第一行这个一维数组的大小
		printf("sizeof(a[0] + 1) = %zd\n", sizeof(a[0] + 1));
		// 4 or 8，对应 2 的地址
		printf("sizeof(*(a[0] + 1)) = %zd\n", sizeof(*(a[0] + 1)));
		// 4，对应 int 2
		printf("sizeof(a + 1) = %zd\n", sizeof(a + 1));// 4 or 8
		//  跳过第一行的地址（即第二行的地址），对应 5 的地址
		printf("sizeof(*(a + 1)) = %zd\n", sizeof(*(a + 1)));
		// 16 对第二行地址的解引用，整个第二行的大小，同 a[1]
		printf("sizeof(&a[0] + 1) = %zd\n", sizeof(&a[0] + 1));
		// 4 or 8 第二行的地址（注意不是二级指针）
		printf("sizeof(*(&a[0] + 1)) = %zd\n", sizeof(*(&a[0] + 1)));
		// 16 第二行的大小
		printf("sizeof(*a) = %zd\n", sizeof(*a)); // 16 第一行的大小
		printf("sizeof(a[3]) = %zd\n", sizeof(a[3]));
		// 虽然越界，但是只是计算内层大小，不会越界访问值
		// 16 越界计算，但是数组 a 每一行的类型是 int [4]，
		// 所以 a[3] 会计算 4 个 int 类型，大小是 16 个字节

		print_end_line("二维数组的情况复习题部分结束");
	}

	// 结构体指针学习题目
	{
		print_start_line("结构体指针学习题目部分开始");
		struct Test
		{
			int Num; // 4
			char* pcName; // 4 or 8
			short sDate; // 2
			char cha[2]; // 2
			short sBa[4]; // 8
		}*p = (struct Test*)0x100000; // 假设地址为 0x100000
		//假设p的值为 0x100000。如下表表达式的值分别为多少?
		//已知,结构体 Test 类型的变量大小是 20 个字节
		//x86
		printf("%p\n", p + 0x1);
		// 0x100000 + 20 -> 0x100014   (x86 平台)
		printf("%p\n", (unsigned long)p + 0x1); // 整型加 1 这里有警告
		// 1048576 + 1 -> 1048577
		// 0x100000 + 1 -> 0x100001   (地址打印)
		printf("%p\n", (unsigned int*)p + 0x1); // 整型指针加 1
		// 0x100000 + 4 -> 0x100004

		print_end_line("结构体指针学习题目部分结束");
	}

	// 指针与整型转换题目
	{
		print_start_line("指针与整型转换部分开始");
		int a[4] = { 1,2,3,4 };
		int* ptr1 = (int*)(&a + 1);
		int* ptr2 = (int*)((long long)a + 1);
		// x64 平台使用 long long，x86 平台使用 int（原题） 或 long
		printf("ptr1[-1] = %x，ptr2[0] = %x\n", ptr1[-1], *ptr2);//十六进制值打印
		/* 注意：(int)a 只能在 x86 平台上运行，因为 8 字节指针转 4 字节 int 会丢失信息
			ptr1[-1] == *(ptr - 1)，这里结果为 4
			(int)a + 1 即 a 的地址当做整型值加 1，注意地址加一只跳过一个字节
			因为 int 是 4 个字节，vs 编译器内存存储是小端模式，即地址的低位在前
			a 的内存图示为：01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00
			(int*)((int)a + 1) 指向 01 后面的 00 即 int 类型的 ptr2 指向 01 后面的 00
			所以 *ptr2 即 00 00 00 02，小端模式，取出后为 0x 02 00 00 00 */
		printf("-----------------------------\n");

		int b[5][5];
		int(*pb)[4];
		pb = b; // pb 指向二维数组 b 的首元素地址
		printf("%p\t%d\n", &pb[4][2] - &b[4][2], &pb[4][2] - &b[4][2]);// 有警告
		/* pb 的每行有 4 个元素，所以这里的地址减法如下：
		&pb[4][2] - &b[4][2] = (4 * 4 + 2) - (4 * 5 + 2) = -4
		就是 b 的首元素地址加 18 个 int 类型大小减去
		b 的首元素地址加 22 个 int 类型大小，结果为 -4
		因为 %p 打印的是地址，这里会将 -4 的补码当成地址打印，
			计算过程（x86 内存为例 0x 数值 8 位）：
			10000000 00000000 00000000 00000100   -4 的原码
			11111111 11111111 11111111 11111011   -4 的反码
			11111111 11111111 11111111 11111100   -4 的补码
			 f   f    f   f    f   f    f   c    十六进制转化（x64 平台为 16 位）*/
		printf("-----------------------------\n");

		char* c[] = { "ENTER","NEW","POINT","FIRST" };
		char** cp[] = { c + 3, c + 2, c + 1, c };
		char*** cpp = cp;
		printf("%s\n", **++cpp); // 打印 "POINT"，之后 cpp 指向 cp 中的 c + 2
		printf("%s\n", *-- * ++cpp + 3); // 打印 "ER"，之后 cpp 指向 cp 中的 c + 1
		// 这里 *++cpp 指向 cp 中的 c + 1，然后 -- 操作为 c + 1 - 1 = c
		printf("%s\n", *cpp[-2] + 3); // 打印 "ST"，cpp 还指向 cp 中的 c + 1
		printf("%s\n", cpp[-1][-1] + 1);// 打印 "EW"，cpp 还指向 cp 中的 c + 1

		print_end_line("指针与整型转换部分结束");
	}
}
